package postionservice

import (
	"errors"
	"fmt"
	"strings"
	"time"
	"yunj/pkg/global"
	"yunj/pkg/yunj/core/builder"
	"yunj/pkg/yunj/core/builder/builderconsts"
	"yunj/pkg/yunj/core/builder/builderdto"
	"yunj/pkg/yunj/core/builder/builderenum"
	"yunj/pkg/yunj/core/builder/form"
	"yunj/pkg/yunj/core/builder/form/button"
	"yunj/pkg/yunj/core/builder/form/field"
	"yunj/pkg/yunj/core/builder/table"
	"yunj/pkg/yunj/core/builder/table/col"
	"yunj/pkg/yunj/core/builder/table/col/action"
	"yunj/pkg/yunj/core/builder/table/deftoolbar"
	"yunj/pkg/yunj/core/builder/table/toolbar"
	"yunj/pkg/yunj/util"

	"yunj/pkg/yunj/app/dto"
	"yunj/pkg/yunj/app/enum/stateenum"
	"yunj/pkg/yunj/app/model"
	"yunj/pkg/yunj/app/validator/admin/postionvalidator"

	"github.com/gin-gonic/gin"
	"github.com/worklz/go-validate"
	"gorm.io/gorm"
)

type postionPage struct {
}

var PostionPage *postionPage

// 列表是否为树形
func (s *postionPage) listBuilderIsTree(stateCode any) bool {
	// 回收站开启分页、不使用树形展示
	return util.Float64ToUints(stateCode) != stateenum.RECYLE_BIN
}

// 列表构建器
func (s *postionPage) ListBuilder(ctx *gin.Context) *builder.Table {
	return builder.NewTable(ctx, "PostionList").
		State([]builderdto.TableState{
			{Code: stateenum.NORMAL, Title: "正常"},
			{Code: stateenum.RECYLE_BIN, Title: "回收站"},
		}).
		Page(false).
		Tree(func(t *builder.Table, state *builderdto.TableState) (res bool, err error) {
			res = s.listBuilderIsTree(state.Code)
			return
		}).
		Filter(field.NewText("keywords", "关键词").SetPlaceholder("模糊匹配，名称/标识")).
		Toolbar(func(t *builder.Table, state *builderdto.TableState) (actions []toolbar.ActionInterface, err error) {
			actions = []toolbar.ActionInterface{
				toolbar.NewOpenPopup("add", "添加").SetClass("layui-icon-add-circle").
					SetUrl(util.BuildAdminUrl("/postion/list/add")).SetAuth("yunj_postion_list_add"),
			}
			if state.Code == stateenum.RECYLE_BIN {
				actions = append(actions,
					toolbar.NewAsyncEvent(builderenum.TABLE_EVENT_TYPE_NORMAL, "恢复正常").SetDropdown(true).SetAuth("yunj_postion_list_normal"),
					toolbar.NewAsyncEvent(builderenum.TABLE_EVENT_TYPE_DELETED, "永久删除").SetDropdown(true).SetAuth("yunj_postion_list_deleted"),
				)
			} else {
				actions = append(actions,
					toolbar.NewOpenPopup(builderenum.TABLE_EVENT_TYPE_SORT, "排序").SetClass("yunj-icon-sort").
						SetUrl(util.BuildAdminUrl("/postion/list/sort")).SetAuth("yunj_postion_list_sort"),
					toolbar.NewAsyncEvent(builderenum.TABLE_EVENT_TYPE_RECYLE_BIN, "移入回收站").SetDropdown(true).SetAuth("yunj_postion_list_recyle_bin"),
				)
			}
			return
		}).
		DefaultToolbar([]deftoolbar.ActionInterface{
			deftoolbar.NewFilter(),
			deftoolbar.NewExport().SetAuth("yunj_postion_list_export"),
		}).
		Cols(func(t *builder.Table, state *builderdto.TableState) (cols []table.ColInterface, err error) {
			roleEnumOptions, err := model.GetNormalRoleTableColOptions()
			if err != nil {
				return
			}
			cols = []table.ColInterface{
				col.NewCheckbox("id", "ID"),
				col.NewText("name", "名称"),
				col.NewText("code", "标识"),
				col.NewText("intro", "简介"),
				col.NewTrigger("member_count", "成员数").SetAlign(builderconsts.ALIGN_CENTER).SetMaxWidth(80).
					SetEventType(builderconsts.EVENT_OPEN_TAB).SetEventTitle("成员管理").SetEventUrl(fmt.Sprintf("`%s?postion_ids=`+ d.id", util.BuildAdminUrl("/member/list"))),
				col.NewEnum("role_ids", "角色").SetOptions(roleEnumOptions),
			}
			actionCol := col.NewAction("操作").SetOptions([]action.ActionInterface{
				action.NewOpenPopup("edit", "详情").SetClass("layui-icon-survey").SetUrl(util.BuildAdminUrl("/postion/list/edit")).SetAuth("yunj_postion_list_edit"),
			})
			if state.Code == stateenum.RECYLE_BIN {
				actionCol.Options = append(actionCol.Options,
					action.NewAsyncEvent(builderenum.TABLE_EVENT_TYPE_NORMAL, "恢复正常").SetDropdown(true).SetAuth("yunj_postion_list_normal"),
					action.NewAsyncEvent(builderenum.TABLE_EVENT_TYPE_DELETED, "永久删除").SetDropdown(true).SetAuth("yunj_postion_list_deleted").SetConfirmFormFields(field.NewTxt("tips", "确认删除？")),
				)
			} else {
				actionCol.Options = append(actionCol.Options, action.NewAsyncEvent(builderenum.TABLE_EVENT_TYPE_RECYLE_BIN, "移入回收站").SetDropdown(true).SetAuth("yunj_postion_list_recyle_bin"))
			}
			cols = append(cols, actionCol)
			return
		}).
		Validator(func(t *builder.Table) (validate.ValidatorInterface, error) {
			v := &postionvalidator.Postion{}
			v.InitValidator(v)
			return v, nil
		}).
		Count(func(t *builder.Table, requestParam *builderdto.TableRequestParam) (res builderdto.TableCountResult, err error) {
			// 状态数量查询
			query := global.MySQL.Model(&model.AdminPostion{})
			var filterFormParam dto.AdminPostionPageListFilterFormParam
			util.MapToStruct(requestParam.FilterData(), &filterFormParam)
			s.listBuilderFilterQuery(query, requestParam, &filterFormParam)
			err = query.Count(&res.Count).Error
			if err != nil {
				err = fmt.Errorf("数据量查询异常！%v", err)
				return
			}
			return
		}).
		Items(func(t *builder.Table, requestParam *builderdto.TableRequestParam) (res builderdto.TableItemsResult, err error) {
			query := global.MySQL.Select("*")
			var filterFormParam dto.AdminPostionPageListFilterFormParam
			util.MapToStruct(requestParam.FilterData(), &filterFormParam)
			s.listBuilderFilterQuery(query, requestParam, &filterFormParam)
			if s.listBuilderIsTree(requestParam.State) {
				query.Order("sort asc")
			} else {
				query.Order("updated_at desc")
			}
			var postionItems []*model.AdminPostion
			if err = query.Find(&postionItems).Error; err != nil {
				err = fmt.Errorf("数据查询异常！%v", err)
				return
			}
			res.Items, err = s.listBuilderItemsHandle(postionItems)
			return
		}).
		Event(func(t *builder.Table, requestParam *builderdto.TableRequestParam) (res builderdto.TableEventResult, err error) {
			handleEventRes, err := builderenum.HandleEvent(ctx, &model.AdminPostion{}, requestParam.EventCode, requestParam.Pks, func(param *builderenum.EventDbParam, e *builderenum.Event, pks []any) (err error) {
				if e.Value == builderenum.TABLE_EVENT_TYPE_DELETED {
					param.Data["code"] = gorm.Expr("concat(`code`, '_del_', ?)", time.Now().Format("20060102150405"))
				}
				return
			})
			if err != nil {
				return
			}
			if handleEventRes != nil {
				res.ResponseJsonData = handleEventRes
				return
			}
			err = fmt.Errorf("异常事件[%v]", requestParam.EventCode)
			return
		})
}

type PostionMemberCount struct {
	PostionId   uint64
	MemberCount uint64
}

// 列表数据项处理
func (s *postionPage) listBuilderItemsHandle(items []*model.AdminPostion) (resItems []*dto.AdminPostionPageListItemResponse, err error) {
	if len(items) <= 0 {
		return
	}
	ids := make([]uint64, 0, len(items))
	for _, item := range items {
		ids = append(ids, item.Id)
	}
	tablePrefix := global.Config.Database.TablePrefix
	// 关联角色ids
	var postionRoles []*model.AdminPostionRole
	err = global.MySQL.Model(&model.AdminPostionRole{}).Where("postion_id in ?", ids).Find(&postionRoles).Error
	if err != nil {
		err = fmt.Errorf("关联角色查询异常！%v", err)
		return
	}
	postionRoleIds := map[uint64][]uint64{}
	for _, postionRole := range postionRoles {
		if _, exists := postionRoleIds[postionRole.PostionId]; !exists {
			postionRoleIds[postionRole.PostionId] = []uint64{}
		}
		postionRoleIds[postionRole.PostionId] = append(postionRoleIds[postionRole.PostionId], postionRole.RoleId)
	}
	// 成员数
	var postionMemberCounts []*PostionMemberCount
	err = global.MySQL.Table(tablePrefix+"admin_member_department_postion mdp").
		Joins("join "+tablePrefix+"admin_department_postion as dp on dp.id=mdp.department_postion_id").
		Where(
			"dp.postion_id in ? and dp.state = ? and exists (?) and exists (?)",
			ids,
			stateenum.NORMAL,
			global.MySQL.Table(tablePrefix+"admin_member m").Select("1").Where("m.id = mdp.member_id and m.state = ?", stateenum.NORMAL),
			global.MySQL.Table(tablePrefix+"admin_department d").Select("1").Where("d.id = dp.department_id and d.state = ?", stateenum.NORMAL),
		).Group("dp.postion_id").Select("dp.postion_id, count(distinct mdp.member_id) as member_count").Find(&postionMemberCounts).Error
	if err != nil {
		err = fmt.Errorf("角色成员数据查询异常！%v", err)
		return
	}
	memberCounts := map[uint64]uint64{}
	for _, postionMemberCount := range postionMemberCounts {
		memberCounts[postionMemberCount.PostionId] = postionMemberCount.MemberCount
	}

	resItems = make([]*dto.AdminPostionPageListItemResponse, 0, len(items))
	for _, item := range items {
		resItem := &dto.AdminPostionPageListItemResponse{
			Id:    item.Id,
			Name:  item.Name,
			Code:  item.Code,
			Intro: item.Intro,
		}
		if roleIds, exists := postionRoleIds[item.Id]; exists {
			resItem.RoleIds = roleIds
		}
		// member_count
		if memberCount, exists := memberCounts[item.Id]; exists {
			resItem.MemberCount = memberCount
		}
		resItems = append(resItems, resItem)
	}
	return
}

// 列表过滤条件查询对象
func (s *postionPage) listBuilderFilterQuery(query *gorm.DB, requestParam *builderdto.TableRequestParam, filterFormParam *dto.AdminPostionPageListFilterFormParam) {
	// ids
	if len(requestParam.Pks) > 0 {
		query.Where("`id` in ?", requestParam.Pks)
	}
	// state
	stateWhere := stateenum.NORMAL
	if stateCode, ok := stateenum.IsValue(requestParam.State); ok {
		stateWhere = stateCode
	}
	query.Where("state = ?", stateWhere)
	// keywords
	if filterFormParam.Keywords != "" {
		keywordsWhere := "%" + filterFormParam.Keywords + "%"
		query.Where("`name` like ? or code like ?", keywordsWhere, keywordsWhere)
	}
}

// 表单构建器
func (s *postionPage) FormBuilder(ctx *gin.Context, isEdit bool) (formBuilder *builder.Form) {
	formBuilder = builder.NewForm(ctx, "PostionForm").
		Field(func(f *builder.Form, tab *builderdto.FormTab) (fields []form.FieldInterface, err error) {
			fields = []form.FieldInterface{
				field.NewText("name", "名称").SetVerify("required|max:100"),
				field.NewText("code", "标识").SetVerify("max:50").SetDesc("全局唯一"),
				field.NewTextarea("intro", "简介").SetVerify("max:200"),
				field.NewDropdownSearch("role_ids", "角色").SetOptionsApi(util.BuildAdminUrl("/role/dropdownSearchOptions")).SetMulti(true),
			}
			if isEdit {
				fields = append(fields, field.NewHidden("id"))
			}
			// 设置栅格布局
			for _, v := range fields {
				v.SetFieldGrid([]any{12, 6, "6 l3 r3"})
			}
			return
		}).
		Button(func(f *builder.Form) (buttons []form.ButtonInterface, err error) {
			if isEdit {
				buttons = []form.ButtonInterface{
					button.NewReset(),
					button.NewSubmit(),
				}
			} else {
				buttons = []form.ButtonInterface{
					button.NewClear(),
					button.NewSubmit(),
				}
			}
			return
		}).
		Validator(func(f *builder.Form) (validate.ValidatorInterface, error) {
			v := &postionvalidator.Postion{}
			v.InitValidator(v)
			return v, nil
		}).
		Submit(func(f *builder.Form, values map[string]any) (res builderdto.FormSubmitResult, err error) {
			var dbPostion *model.AdminPostion
			dbPostion = util.MapKeyValue(values, "dbPostion", dbPostion)
			var dbPostionRoles []*model.AdminPostionRole
			dbPostionRoles = util.MapKeyValue(values, "dbPostionRoles", dbPostionRoles)
			if dbPostion.Id > 0 {
				// 修改
				err = global.MySQL.Select("name", "code", "intro").Save(dbPostion).Error
				if err != nil {
					err = fmt.Errorf("编辑失败！%v", err.Error())
					return
				}
			} else {
				// 新增
				err = global.MySQL.Create(dbPostion).Error
				if err != nil {
					err = fmt.Errorf("新增失败！%v", err.Error())
					return
				}
				// 赋值岗位自增id
				for _, dbPostionRole := range dbPostionRoles {
					dbPostionRole.PostionId = dbPostion.Id
				}
			}
			// 修改岗位角色数据
			err = global.MySQL.Transaction(func(tx *gorm.DB) (err error) {
				// 先删除，后增加
				if err = global.MySQL.Where("postion_id = ?", dbPostion.Id).Delete(&model.AdminPostionRole{}).Error; err != nil {
					err = fmt.Errorf("岗位角色数据处理异常，请刷新页面后重试！%v", err)
					return
				}
				if err = global.MySQL.CreateInBatches(dbPostionRoles, len(dbPostionRoles)).Error; err != nil {
					err = fmt.Errorf("岗位角色数据保存异常，请刷新页面后重试！%v", err)
					return
				}
				return
			})
			res.Reload = true
			return
		})

	if isEdit {
		formBuilder.Load(func(f *builder.Form) (res any, err error) {
			id := util.QueryUint64Param(ctx, "id")
			if id <= 0 {
				err = fmt.Errorf("异常数据")
				return
			}
			var data model.AdminPostion
			dbErr := global.MySQL.Where("`id` = ? and state <> ?", id, stateenum.DELETED).First(&data).Error
			if (dbErr != nil && !errors.Is(dbErr, gorm.ErrRecordNotFound)) || data.Id <= 0 {
				err = fmt.Errorf("数据获取异常！%v", dbErr)
				return
			}
			resData := dto.AdminPostionPageFormResponse{
				Id:    data.Id,
				Name:  data.Name,
				Code:  data.Code,
				Intro: data.Intro,
			}
			// 获取关联角色ids
			err = global.MySQL.Model(&model.AdminPostionRole{}).Where("postion_id = ?", resData.Id).Pluck("role_id", &resData.RoleIds).Error
			if err != nil {
				err = fmt.Errorf("角色数据获取异常！%v", err)
				return
			}
			res = util.StructToMap(resData)
			return
		})
	}
	return
}

// 下拉搜索选项
func (s *postionPage) DropdownSearchOptions(ctx *gin.Context) (options []builderdto.Option, err error) {
	param := util.QueryParams(ctx)
	query := global.MySQL.Where("state = ?", stateenum.NORMAL).Limit(20).Select("`id`,name,code")
	if values, exists := param["values"]; exists {
		if values, ok := values.(string); ok && values != "" {
			if ids := util.ToUint64Slice(strings.Split(values, ",")); len(ids) > 0 {
				query = query.Where("`id` in ?", ids)
			}
		}
	}
	if keywords, exists := param["keywords"]; exists {
		if keywords, ok := keywords.(string); ok && keywords != "" {
			likeKeywords := "%" + keywords + "%"
			query = query.Where("(`name` like ? or `code` like ?)", likeKeywords, likeKeywords)
		}
	}
	var items []model.AdminPostion
	dbErr := query.Find(&items).Error
	if dbErr != nil {
		err = fmt.Errorf("数据查询异常！%v", dbErr)
		return
	}
	options = make([]builderdto.Option, 0, len(items))
	for _, item := range items {
		txt := item.Name
		if item.Code != "" {
			txt += fmt.Sprintf("(%s)", item.Code)
		}
		options = append(options, builderdto.Option{Value: item.Id, Label: txt})
	}
	return
}
